package com.example.tool.uicomponents;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.example.tool.es.Es7Client;
import com.jfoenix.controls.*;
import io.datafx.controller.ViewController;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.*;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Modality;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@ViewController(value = "/fxml/ui/Button.fxml", title = "Material Design Example")
public class ButtonController {

    @FXML
    private HBox flowBox;

    @FXML
    private JFXTextField taskIdText;


    private String taskId;

    private Date startTime;


    private List<StepNode> stepNodeList;

    @Data
    private class Step {
        private String serviceName;
        private String className;

        private String methodName;

        private String taskId;

        private String remark;

        private Integer step;
        private Integer status;

        private Integer stage;

        private Date begin;

        private Date end;

        private Integer cost;

        private boolean finish;

        private List<StepNode> stepNodes;
    }

    @Data
    private class StepNode {
        private String taskId;
        private String logId;
        private Integer status;
        private boolean finish;
        private Integer cost;
        private String result;
    }

    @Data
    static class Task {
        List<Step> stepList;
        private String source;
        private Integer taskType;

        public void addStep(Step st) {
            if (stepList == null) {
                stepList = new ArrayList<>();
            }
            stepList.add(st);
        }
    }

    @Data
    static class StepNodeEntity{
        private List<StepNode> stepNodeList;
        private Integer page;
        private boolean next;
        public int  nextPage(){
            return next?page+1:page-1;
        }
    }

    @PostConstruct
    public void init() {
        //taskIdText.focusColorProperty().addListener((observable, oldValue, newValue) -> System.out.println(newValue));
    }

    @FXML
    public void searchLog() throws IOException, ParseException {
        taskId = taskIdText.getText();
        if (taskId != null && !taskId.isEmpty()) {
            taskId = taskId.trim();
        }
        if (taskId != null && !taskId.isEmpty()) {
            // 查询log
            //CC1807936849259159552
            Task task = queryAndObj(taskId);
            startTime = task.getStepList().get(0).getBegin();
            flowBox.getChildren().clear();
            List<Step> stepList = task.getStepList();
            int stepIndex =1;
            for (Step step : stepList) {
                JFXButton button = buildButton(step.getRemark(), 80, "#ffffff", "#2d2c2c", step.getStatus(), step.getStage());
                button.setId(step.getTaskId()+stepIndex);
                stepIndex+=1;
                VBox vBox = new VBox();
                vBox.setAlignment(Pos.TOP_CENTER);
                vBox.getChildren().add(button);
                vBox.setUserData(step);
                List<StepNode> stepNodes = step.getStepNodes();
                stepNodeList = stepNodes;
                addStepNodeButton(stepNodeList,vBox,1,true);
                flowBox.getChildren().add(vBox);
                flowBox.getChildren().add(buildPaddingLabel(200, 20));
            }


        }
    }

    private void addStepNodeButton(List<StepNode> stepNodeList,VBox parent,int pageIndex,boolean next) {
        SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        int nodeSize = stepNodeList.size();
        boolean markIndex = nodeSize>1;
        Step  step = (Step) parent.getUserData();
        if(nodeSize<=3) {
            addStepNodeByIndex(0,Math.min(nodeSize, 3),parent,step,markIndex,yyyyMMddHHmmss);
        }else{
            int startIndex  = Math.max(pageIndex*3-3,0);
            int endIndex = Math.min(startIndex+3,nodeSize);
            addStepNodeByIndex(startIndex,endIndex,parent,step,markIndex,yyyyMMddHHmmss);
        }
        if(nodeSize>3){
            parent.getChildren().add(buildPaddingLabel(10, 50));
            //vBox.setStyle("-fx-text-fill: #16e07b");
            JFXButton nextLog = buildButton(next?"next":"before", 50, "#ffffff", "#2d2c2c", 1, step.getStage());
            StepNodeEntity stepNodeEntity = new StepNodeEntity();
            stepNodeEntity.setStepNodeList(stepNodeList);
            stepNodeEntity.setPage(pageIndex);

            if(next && pageIndex * 3 < nodeSize){
                stepNodeEntity.setNext(true);
            }else if(!next && pageIndex<=0){
                stepNodeEntity.setNext(true);
            }else{
                stepNodeEntity.setNext(false);
            }
            nextLog.setUserData(stepNodeEntity);
            nextLog.setOnAction(event -> {
                JFXButton eventSource = (JFXButton) event.getSource();
                StepNodeEntity userData = (StepNodeEntity) eventSource.getUserData();
                StepNode stepNode = userData.getStepNodeList().get(0);
                String tid = stepNode.getTaskId();

                VBox vBox = (VBox) eventSource.getParent();
                ObservableList<Node> children = vBox.getChildren();
                List<Integer> removeIndex = new ArrayList<>();
                for (int i = 0; i < children.size(); i++) {
                    Node node = children.get(i);
                    if(node instanceof JFXButton){
                        String id = node.getId();
                        if(StringUtils.isEmpty(id) || !id.startsWith(tid)){
                            removeIndex.add(i);
                        }
                    }else{
                        removeIndex.add(i);
                    }
                }
                for (int  index : removeIndex.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList())) {
                    vBox.getChildren().remove(index);
                }
                addStepNodeButton(stepNodeList,vBox,userData.nextPage(),userData.isNext());
            });
            parent.getChildren().add(nextLog);
        }
    }

    public void addStepNodeByIndex(int startIndex, int endIndex, VBox parent, Step step, boolean markIndex, SimpleDateFormat yyyyMMddHHmmss){
        int nodeIndex = startIndex+1;
        for (StepNode stepNode : stepNodeList.subList(startIndex,endIndex)) {
            parent.getChildren().add(buildPaddingLabel(10, 50));
            JFXButton stepLog = buildButton(markIndex ? ("任务" + nodeIndex) : "任务", 50, "#ffffff", "#2d2c2c", stepNode.getStatus(), step.getStage());
            //stepLog.setAlignment(Pos.TOP_CENTER);
            stepLog.setId(stepNode.getLogId());

            buildButtonAction(stepNode, stepLog, step, yyyyMMddHHmmss);

            parent.getChildren().add(stepLog);
            nodeIndex++;
        }
    }

    private void buildButtonAction(StepNode stepNode, JFXButton stepLog, Step step, SimpleDateFormat yyyyMMddHHmmss) {
        stepLog.setUserData(JSONObject.toJSONString(stepNode));
        stepLog.setOnAction(event -> {
            JFXButton bt = (JFXButton) event.getSource();
            String userData = (String) bt.getUserData();
            StepNode node = JSONObject.parseObject(userData, StepNode.class);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd");
            SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
            searchBuilder.query(QueryBuilders.boolQuery()
                            .filter(QueryBuilders.termQuery("logId", node.getLogId())))
                    .size(9000)
                    .sort(
                            Arrays.asList(
                                    new FieldSortBuilder("timestamp").order(SortOrder.ASC),
                                    new FieldSortBuilder("id").order(SortOrder.ASC))
                    )
            ;
            try {
                StringBuilder logMsg = new StringBuilder();
                SearchHits searchHits = Es7Client.searchList(searchBuilder, "dkd_chain_monitor_log_record_" + simpleDateFormat.format(startTime));
                for (SearchHit hit : searchHits.getHits()) {
                    JSONObject log = JSONObject.parseObject(hit.getSourceAsString());

                    String[] msgArr = log.get("msg").toString().split("#msg:");
                    logMsg.append(
                                    yyyyMMddHHmmss.format(new Date(log.getLong("timestamp"))))
                            .append("\t")
                            .append(msgArr[0])
                            .append("\t")
                            .append(msgArr.length > 1 ? msgArr[1] : "")
                            .append("\n")
                            .append("\n")
                    ;



                }
                JFXAlert alert = new JFXAlert(bt.getScene().getWindow());
                alert.initModality(Modality.WINDOW_MODAL);
                alert.setOverlayClose(false);
                JFXDialogLayout layout = new JFXDialogLayout();
                layout.setHeading(new Label("日志"));
                JFXTextArea textArea = new JFXTextArea();

                textArea.setWrapText(true);
                textArea.setEditable(false);
                textArea.setMinWidth(1300);
                //textArea.setMinHeight(800);
                textArea.setStyle("-fx-font-size: 15px;-fx-text-fill: #14306b;-fx-background-color: #ffffff");
                layout.setMinWidth(1300);
                layout.setMinHeight(800);
                textArea.setText(logMsg.toString());
                layout.setBody(textArea);


                JFXButton closeButton = new JFXButton("关闭");
                closeButton.getStyleClass().add("dialog-accept");
                closeButton.setOnAction(e -> alert.hideWithAnimation());
                layout.setActions(closeButton);
                layout.setMinWidth(1300);
                layout.setMaxHeight(800);
                alert.getDialogPane().setStyle(".jfx-alert-content-container{ -fx-background-color: #2d2c2c;}");
                alert.setContent(layout);
                alert.setWidth(1300);
                alert.setHeight(800);
                alert.show();

            } catch (IOException e) {
                throw new RuntimeException(e);
            }


        });


        String serviceName = step.getServiceName();
        String remark = step.getRemark();
        Date begin = step.getBegin();
        Date end = step.getEnd();
        StringBuilder builder = new StringBuilder();
        builder.append("服务:").append(serviceName)
                .append("\n")
                .append("备注:").append(remark)
                .append("\n")
                .append("开始时间:").append(yyyyMMddHHmmss.format(begin))
                .append("\n")
                .append("结束时间:").append(yyyyMMddHHmmss.format(end))
                .append("\n")
                .append("消耗时间:(s)").append(step.getCost() / 1000);

        stepLog.setTooltip(new Tooltip(builder.toString()));
    }

    public javafx.scene.control.Label buildPaddingLabel(int width, int height) {
        javafx.scene.control.Label label = new javafx.scene.control.Label();
        label.setPrefSize(width, height);
        return label;
    }

    private JFXButton buildButton(String text, int radius, String textFill, String backgroundFill, Integer status, Integer stage) {
        JFXButton button = new JFXButton();
        button.setText(text);
        button.setWrapText(true);
        //prefWidth="100" prefHeight="40" buttonType="RAISED"
        button.setPrefSize(radius * 2, radius * 2);
        button.setButtonType(JFXButton.ButtonType.RAISED);
        Circle circle = new Circle(radius);
        button.setShape(circle);
        button.setTextFill(Paint.valueOf(textFill));
        if (status < 1) {
            backgroundFill = "#e02716";
        }

        button.setBackground(new Background(new BackgroundFill(Paint.valueOf(backgroundFill), null, null)));


        button.setStyle("-fx-font-size:14px;");
        BorderWidths DEFAULT = new BorderWidths(3, 3, 3, 3, false, false, false, false);
        String borderColor = "#e83972";
        if (stage == 1) {
            borderColor = "#16e07b";
        }
        if (stage == 2) {
            borderColor = "#1656e0";
        }
        BorderStroke borderStroke = new BorderStroke(Paint.valueOf(borderColor), BorderStrokeStyle.DASHED, null, DEFAULT);
        button.setBorder(new Border(borderStroke));
        return button;
    }

    private Task queryAndObj(String taskId) throws IOException, ParseException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.boolQuery().filter(QueryBuilders.termQuery("id", taskId)));
        SearchHits taskHits = Es7Client.searchList(sourceBuilder, "dkd_chain_monitor_task_record_2024_07");
        SearchHit task = taskHits.getHits()[0];
        JSONObject taskObj = JSONObject.parseObject(task.getSourceAsString());
        Task t = new Task();
        JSONArray steps = taskObj.getJSONArray("steps");
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (int i = 0; i < steps.size(); i++) {
            JSONObject step = steps.getJSONObject(i);
            Step st = new Step();
            st.setTaskId(step.getString("taskId"));
            st.setServiceName(step.getString("serviceName"));
            st.setRemark(step.getString("remark"));
            st.setClassName(step.getString("className"));
            st.setMethodName(step.getString("methodName"));
            st.setStep(step.getInteger("step"));
            st.setStatus(step.getInteger("status"));
            st.setStage(step.getInteger("stage"));
            st.setBegin(simpleDateFormat.parse(step.getString("startTime")));
            st.setEnd(simpleDateFormat.parse(step.getString("endTime")));
            st.setCost(step.getInteger("cost"));
            st.setFinish(step.getBoolean("finish"));

            JSONArray stepLogs = step.getJSONArray("stepLogs");
            List<StepNode> stepNodeList = new ArrayList<>();
            for (int i1 = 0; i1 < stepLogs.size(); i1++) {
                JSONObject stepLog = stepLogs.getJSONObject(i1);
                StepNode stepNode = new StepNode();
                stepNode.setCost(stepLog.getInteger("cost"));
                stepNode.setLogId(stepLog.getString("logId"));
                stepNode.setTaskId(stepLog.getString("taskId"));
                stepNode.setFinish(stepLog.getBoolean("finish"));
                stepNode.setStatus(stepLog.getInteger("status"));
                if (stepLog.containsKey("result")) {
                    stepNode.setResult(stepLog.getString("result"));
                } else {
                    stepNode.setResult("success");
                }
                stepNodeList.add(stepNode);
            }
            st.setStepNodes(stepNodeList);
            t.addStep(st);
        }
        return t;
    }


}
